/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package elaborate.tag_analysis.client.applications.flickr;

import elaborate.tag_analysis.client.TagEntry;
import elaborate.tag_analysis.client.applications.DaoFactory;
import elaborate.tag_analysis.client.applications.ResourceHandler;
import elaborate.tag_analysis.client.ui.model.ResourceTreeNode;
import elaborate.tag_analysis.client.utils.resource_logs.Category;
import elaborate.tag_analysis.client.utils.resource_logs.ResourceLog;
import elaborate.tag_analysis.client.utils.resource_logs.ResourceLogs;
import elaborate.tag_analysis.client.utils.resource_logs.ResourceStatus;
import elaborate.tag_analysis.client.utils.resource_logs.TagLog;
import elaborate.tag_analysis.model.Resource;
import elaborate.tag_analysis.model.TURRelation;
import elaborate.tag_analysis.model.Tag;
import elaborate.tag_analysis.restws.client.ResourceDao;
import elaborate.tag_analysis.restws.client.TURRelationDao;
import elaborate.tag_analysis.restws.client.TagDao;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 *
 * @author lendle
 */
public class FlickrResourceHandler implements ResourceHandler {

    protected ResourceLogs resourceLogs;
    private List<String> categories = new ArrayList<String>();
    private FlickrParams params = null;
    private DaoFactory daoFactory=null;

    public FlickrResourceHandler(FlickrParams params, ResourceLogs resourceLogs, DaoFactory daoFactory) {
        this.resourceLogs = resourceLogs;
        categories.add("Hot List");
        this.params = params;
        this.daoFactory=daoFactory;
    }

    /**
     * Get the value of resourceLogs
     *
     * @return the value of resourceLogs
     */
    public ResourceLogs getResourceLogs() {
        return resourceLogs;
    }

    /**
     * Set the value of resourceLogs
     *
     * @param resourceLogs new value of resourceLogs
     */
    public void setResourceLogs(ResourceLogs resourceLogs) {
        this.resourceLogs = resourceLogs;
    }

    @Override
    public List<String> getCategories() throws Exception {
        return this.categories;
    }

    @Override
    public List<Resource> loadResources(String categoryId) throws Exception {
        List<Resource> results = new ArrayList<Resource>();
        if (resourceLogs.getCategory(categoryId) == null) {
            Category category = new Category();
            category.setId(categoryId);
            resourceLogs.getCategories().add(category);
            if (categoryId != null) {
                //load resources by issuing queries against server
                List<String> hotTagsList = this.getHotTagsList();
                //to prevent duplication
                Map<URL, String> urlMap = new HashMap<URL, String>();
                for (String tag : hotTagsList) {
                    List<URL> urls = this.getPhotoList(tag);
                    int count = 0;
                    for (URL url : urls) {
                        if (urlMap.containsKey(url)) {
                            continue;
                        }
                        Resource _resource = new Resource();
                        _resource.setApplicationId("flickr");
                        _resource.setId(url.toString());
                        _resource.setName(url.toString());
                        _resource.setValue(url.toString());
                        _resource.setParentResourceId(categoryId);
                        results.add(_resource);

                        ResourceLog resourceLog = new ResourceLog();
                        resourceLog.setId(_resource.getId());
                        if (resourceLogs.getResource(categoryId, _resource.getId()) == null) {
                            resourceLogs.getCategory(categoryId).getResources().add(resourceLog);
                        }

                        count++;
                        if (count >= this.params.getMaxURLPerTag()) {
                            break;
                        }
                    }
                    ResourceDao dao = daoFactory.createResourceDao();
                    dao.newResource(results.toArray(new Resource[0]));
                }
            }
        } else {
            //load resources from logs directly
            for (ResourceLog resourceLog : resourceLogs.getCategory(categoryId).getResources()) {
                Resource _resource = new Resource();
                _resource.setApplicationId("flickr");
                _resource.setId(resourceLog.getId());
                _resource.setName(resourceLog.getId());
                _resource.setValue(resourceLog.getId());
                _resource.setParentResourceId(categoryId);
                results.add(_resource);
            }
        }
        return results;
    }

    @Override
    public ResourceStatus getResourceStatus(ResourceTreeNode treeNode) throws Exception {
        if (treeNode.isCategory()) {
            //for category node, this method does not need to be invoked
            return null;
        } else {
            //for non-category resource node
            if (treeNode.getStatus() == ResourceTreeNode.STATUS_UNKNOWN && treeNode.getResource() != null) {
                ResourceLog resourceLog = resourceLogs.getResource(treeNode.getResource().getParentResourceId(), treeNode.getResource().getId());
                if (resourceLog != null && resourceLog.getStatus().equals(ResourceStatus.UNKNOWN) == false) {
                    if (resourceLog.getStatus().equals(ResourceStatus.WITHTAGS)) {
                        return ResourceStatus.WITHTAGS;
                        //treeNode.setStatus(ResourceTreeNode.STATUS_TAGS);
                    } else {
                        return ResourceStatus.NODATA;
                        //treeNode.setStatus(ResourceTreeNode.STATUS_NO_TAGS);
                    }
                } else {
                    TURRelationDao dao = daoFactory.createTURRelationDao();
                    List<TURRelation> list = dao.findByResourceId(treeNode.getResource().getApplicationId(), treeNode.getResource().getId());
                    if (list == null || list.isEmpty()) {
                        //System.out.println("no tag for: "+treeNode.getResource().getId());
                        return ResourceStatus.UNKNOWN;
                        //treeNode.setStatus(ResourceTreeNode.STATUS_UNKNOWN);
                    } else {
                        return ResourceStatus.WITHTAGS;
                    }
                }
            } else {
                return ResourceStatus.UNKNOWN;
            }
        }
    }

    @Override
    public boolean isCategoryStatusVerified(ResourceTreeNode treeNode) throws Exception {
        Category category = resourceLogs.getCategory(treeNode.toString());
        if (category != null && category.getResources().isEmpty() == false) {
            //utilize resourceLog
            for (ResourceLog resourceLog : category.getResources()) {
                if (resourceLog.getStatus().equals(ResourceStatus.UNKNOWN)) {
                    //the status is unknown
                    return false;
                }
            }
        } else {
            if (treeNode.getChildren().isEmpty()) {
                return false;
            } else {
                for (ResourceTreeNode childNode : treeNode.getChildren()) {
                    if (childNode.getStatus() == ResourceTreeNode.STATUS_UNKNOWN) {
                        //the status is unknown
                        return false;
                    }
                }
            }
        }
        //assume the status is known
        return true;
    }

    @Override
    public void saveTags(List<TagEntry> entries, ResourceTreeNode workingNode, ResourceTreeNode categoryNode) throws Exception {
        List<Tag> tags = new ArrayList<Tag>();
        List<TagLog> tagLogs = new ArrayList<TagLog>();
        List<TURRelation> turs = new ArrayList<TURRelation>();
        for (int i = 0; i < entries.size(); i++) {
            String tagStr = (String) entries.get(i).getName();
            Tag tag = new Tag();
            tag.setApplicationId("flickr");
            tag.setId(tagStr);
            tag.setName(tagStr);
            tag.setValue(tagStr);
            tags.add(tag);

            TagLog tagLog = new TagLog();
            tagLog.setId(tagStr);
            tagLogs.add(tagLog);

            TURRelation tur = new TURRelation();
            tur.setApplicationId("flickr");
            //tur.setId(UUID.randomUUID().toString());
            tur.setResourceId(workingNode.getResource().getId());
            tur.setTagId(tagStr);
            tur.setUserId("-1");
            turs.add(tur);
        }
        if (tags.isEmpty() == false) {
            daoFactory.createTagDao().newTag(tags.toArray(new Tag[0]));
            workingNode.setStatus(ResourceTreeNode.STATUS_TAGS);
            resourceLogs.getResource(categoryNode.getResource().getId(), workingNode.getResource().getId()).setStatus(ResourceStatus.WITHTAGS);
        } else {
            workingNode.setStatus(ResourceTreeNode.STATUS_NO_TAGS);
            resourceLogs.getResource(categoryNode.getResource().getId(), workingNode.getResource().getId()).setStatus(ResourceStatus.NODATA);
        }
        if (turs.isEmpty() == false) {
            daoFactory.createTURRelationDao().newTURRelation(turs.toArray(new TURRelation[0]));
        }

        resourceLogs.getResource(categoryNode.getResource().getId(), workingNode.getResource().getId()).getTags().clear();
        resourceLogs.getResource(categoryNode.getResource().getId(), workingNode.getResource().getId()).getTags().addAll(tagLogs);
        //System.out.println("saved!");
    }

    private Document execute(URL url) throws Exception {
        InputStream input = url.openStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(input, "utf-8"));
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new InputSource(reader));
        reader.close();
        return doc;
    }

    private List<String> getHotTagsList() throws Exception {
        List<String> result = new ArrayList<String>();
        Document doc = execute(new URL("http://api.flickr.com/services/rest/?method=flickr.tags.getHotList&api_key=" + this.params.getApplicationKey() + "&period=" + this.params.getHotTagsDuration() + "&count=" + this.params.getHotTagsCount()));
        XPath xpath = XPathFactory.newInstance().newXPath();
        NodeList nodeList = (NodeList) xpath.evaluate("//tag", doc, XPathConstants.NODESET);
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element tagElement = (Element) nodeList.item(i);
            result.add(tagElement.getFirstChild().getNodeValue());
        }
        return result;
    }

    private List<URL> getPhotoList(String tag) throws Exception {
        List<URL> result = new ArrayList<URL>();
        Document doc = execute(new URL("http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=" + this.params.getApplicationKey() + "&tags=" + tag));
        XPath xpath = XPathFactory.newInstance().newXPath();
        NodeList nodeList = (NodeList) xpath.evaluate("//photo", doc, XPathConstants.NODESET);
        for (int i = 0; i < nodeList.getLength(); i++) {
            Element photoElement = (Element) nodeList.item(i);
            //System.out.println("http://www.flickr.com/photos/" + photoElement.getAttribute("owner") + "/" + photoElement.getAttribute("id") + "/");
            result.add(new URL("http://www.flickr.com/photos/" + photoElement.getAttribute("owner") + "/" + photoElement.getAttribute("id") + "/"));
        }
        return result;
    }

    public List<String> getTags4URL(String url) throws Exception {
        String[] parsedURL = url.split("/");
        List<String> result = new ArrayList<String>();
        if (parsedURL != null) {
            for (int i = parsedURL.length - 1; i >= 0; i--) {
                String photoId = parsedURL[i];
                if (photoId != null && photoId.trim().length() > 0) {
                    Document doc = execute(new URL("http://api.flickr.com/services/rest/?method=flickr.photos.getInfo&api_key=" + this.params.getApplicationKey() + "&photo_id=" + photoId));
                    XPath xpath = XPathFactory.newInstance().newXPath();
                    NodeList nodeList = (NodeList) xpath.evaluate("//tag", doc, XPathConstants.NODESET);
                    for (int j= 0; j < nodeList.getLength(); j++) {
                        Element tagElement = (Element) nodeList.item(j);
                        result.add(tagElement.getFirstChild().getNodeValue());
                    }
                    break;
                }
            }
        }
        return result;
    }
}
